<template>
  <view class="uni-data-checklist" :style="{ 'margin-top': isTop + 'px' }">
    <template v-if="!isLocal">
      <view class="uni-data-loading">
        <uni-load-more
          v-if="!mixinDatacomErrorMessage"
          status="loading"
          iconType="snow"
          :iconSize="18"
          :content-text="contentText"
        ></uni-load-more>
        <text v-else>{{ mixinDatacomErrorMessage }}</text>
      </view>
    </template>
    <template v-else>
      <checkbox-group
        v-if="multiple"
        class="checklist-group"
        :class="{ 'is-list': mode === 'list' || wrap }"
        @change="chagne"
      >
        <label
          class="checklist-box"
          :class="[
            'is--' + mode,
            item.selected ? 'is-checked' : '',
            disabled || !!item.disabled ? 'is-disable' : '',
            index !== 0 && mode === 'list' ? 'is-list-border' : '',
          ]"
          :style="item.styleBackgroud"
          v-for="(item, index) in dataList"
          :key="index"
        >
          <checkbox
            class="hidden"
            hidden
            :disabled="disabled || !!item.disabled"
            :value="item[map.value] + ''"
            :checked="item.selected"
          />
          <view
            v-if="(mode !== 'tag' && mode !== 'list') || (mode === 'list' && icon === 'left')"
            class="checkbox__inner"
            :style="item.styleIcon"
          >
            <view class="checkbox__inner-icon"></view>
          </view>
          <view
            class="checklist-content"
            :class="{ 'list-content': mode === 'list' && icon === 'left' }"
          >
            <text class="checklist-text" :style="item.styleIconText">{{ item[map.text] }}</text>
            <view
              v-if="mode === 'list' && icon === 'right'"
              class="checkobx__list"
              :style="item.styleBackgroud"
            ></view>
          </view>
        </label>
      </checkbox-group>
      <radio-group
        v-else
        class="checklist-group"
        :class="{ 'is-list': mode === 'list', 'is-wrap': wrap }"
        @change="chagne"
      >
        <!-- -->
        <label
          class="checklist-box"
          :class="[
            'is--' + mode,
            item.selected ? 'is-checked' : '',
            disabled || !!item.disabled ? 'is-disable' : '',
            index !== 0 && mode === 'list' ? 'is-list-border' : '',
          ]"
          :style="item.styleBackgroud"
          v-for="(item, index) in dataList"
          :key="index"
        >
          <radio
            class="hidden"
            hidden
            :disabled="disabled || item.disabled"
            :value="item[map.value] + ''"
            :checked="item.selected"
          />
          <view
            v-if="(mode !== 'tag' && mode !== 'list') || (mode === 'list' && icon === 'left')"
            class="radio__inner"
            :style="item.styleBackgroud"
          >
            <view class="radio__inner-icon" :style="item.styleIcon"></view>
          </view>
          <view
            class="checklist-content"
            :class="{ 'list-content': mode === 'list' && icon === 'left' }"
          >
            <text class="checklist-text" :style="item.styleIconText">{{ item[map.text] }}</text>
            <view
              v-if="mode === 'list' && icon === 'right'"
              :style="item.styleRightIcon"
              class="checkobx__list"
            ></view>
          </view>
        </label>
      </radio-group>
    </template>
  </view>
</template>

<script>
  /**
   * DataChecklist 数据选择器
   * @description 通过数据渲染 checkbox 和 radio
   * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
   * @property {String} mode = [default| list | button | tag] 显示模式
   * @value default  	默认横排模式
   * @value list		列表模式
   * @value button	按钮模式
   * @value tag 		标签模式
   * @property {Boolean} multiple = [true|false] 是否多选
   * @property {Array|String|Number} value 默认值
   * @property {Array} localdata 本地数据 ，格式 [{text:'',value:''}]
   * @property {Number|String} min 最小选择个数 ，multiple为true时生效
   * @property {Number|String} max 最大选择个数 ，multiple为true时生效
   * @property {Boolean} wrap 是否换行显示
   * @property {String} icon = [left|right]  list 列表模式下icon显示位置
   * @property {Boolean} selectedColor 选中颜色
   * @property {Boolean} emptyText 没有数据时显示的文字 ，本地数据无效
   * @property {Boolean} selectedTextColor 选中文本颜色，如不填写则自动显示
   * @property {Object} map 字段映射， 默认 map={text:'text',value:'value'}
   * @value left 左侧显示
   * @value right 右侧显示
   * @event {Function} change  选中发生变化触发
   */

  export default {
    name: 'uniDataChecklist',
    mixins: [uniCloud.mixinDatacom || {}],
    emits: ['input', 'update:modelValue', 'change'],
    props: {
      mode: {
        type: String,
        default: 'default',
      },

      multiple: {
        type: Boolean,
        default: false,
      },
      value: {
        type: [Array, String, Number],
        default() {
          return '';
        },
      },
      // TODO vue3
      modelValue: {
        type: [Array, String, Number],
        default() {
          return '';
        },
      },
      localdata: {
        type: Array,
        default() {
          return [];
        },
      },
      min: {
        type: [Number, String],
        default: '',
      },
      max: {
        type: [Number, String],
        default: '',
      },
      wrap: {
        type: Boolean,
        default: false,
      },
      icon: {
        type: String,
        default: 'left',
      },
      selectedColor: {
        type: String,
        default: '',
      },
      selectedTextColor: {
        type: String,
        default: '',
      },
      emptyText: {
        type: String,
        default: '暂无数据',
      },
      disabled: {
        type: Boolean,
        default: false,
      },
      map: {
        type: Object,
        default() {
          return {
            text: 'text',
            value: 'value',
          };
        },
      },
    },
    watch: {
      localdata: {
        handler(newVal) {
          this.range = newVal;
          this.dataList = this.getDataList(this.getSelectedValue(newVal));
        },
        deep: true,
      },
      mixinDatacomResData(newVal) {
        this.range = newVal;
        this.dataList = this.getDataList(this.getSelectedValue(newVal));
      },
      value(newVal) {
        this.dataList = this.getDataList(newVal);
        // fix by mehaotian is_reset 在 uni-forms 中定义
        if (!this.is_reset) {
          this.is_reset = false;
          this.formItem && this.formItem.setValue(newVal);
        }
      },
      modelValue(newVal) {
        this.dataList = this.getDataList(newVal);
        if (!this.is_reset) {
          this.is_reset = false;
          this.formItem && this.formItem.setValue(newVal);
        }
      },
    },
    data() {
      return {
        dataList: [],
        range: [],
        contentText: {
          contentdown: '查看更多',
          contentrefresh: '加载中',
          contentnomore: '没有更多',
        },
        isLocal: true,
        styles: {
          selectedColor: '#2979ff',
          selectedTextColor: '#666',
        },
        isTop: 0,
      };
    },
    computed: {
      dataValue() {
        if (this.value === '') return this.modelValue;
        if (this.modelValue === '') return this.value;
        return this.value;
      },
    },
    created() {
      this.form = this.getForm('uniForms');
      this.formItem = this.getForm('uniFormsItem');
      // this.formItem && this.formItem.setValue(this.value)

      if (this.formItem) {
        this.isTop = 6;
        if (this.formItem.name) {
          // 如果存在name添加默认值,否则formData 中不存在这个字段不校验
          if (!this.is_reset) {
            this.is_reset = false;
            this.formItem.setValue(this.dataValue);
          }
          this.rename = this.formItem.name;
          this.form.inputChildrens.push(this);
        }
      }

      if (this.localdata && this.localdata.length !== 0) {
        this.isLocal = true;
        this.range = this.localdata;
        this.dataList = this.getDataList(this.getSelectedValue(this.range));
      } else {
        if (this.collection) {
          this.isLocal = false;
          this.loadData();
        }
      }
    },
    methods: {
      loadData() {
        this.mixinDatacomGet()
          .then((res) => {
            this.mixinDatacomResData = res.result.data;
            if (this.mixinDatacomResData.length === 0) {
              this.isLocal = false;
              this.mixinDatacomErrorMessage = this.emptyText;
            } else {
              this.isLocal = true;
            }
          })
          .catch((err) => {
            this.mixinDatacomErrorMessage = err.message;
          });
      },
      /**
       * 获取父元素实例
       */
      getForm(name = 'uniForms') {
        let parent = this.$parent;
        let parentName = parent.$options.name;
        while (parentName !== name) {
          parent = parent.$parent;
          if (!parent) return false;
          parentName = parent.$options.name;
        }
        return parent;
      },
      chagne(e) {
        const values = e.detail.value;

        let detail = {
          value: [],
          data: [],
        };

        if (this.multiple) {
          this.range.forEach((item) => {
            if (values.includes(item[this.map.value] + '')) {
              detail.value.push(item[this.map.value]);
              detail.data.push(item);
            }
          });
        } else {
          const range = this.range.find((item) => item[this.map.value] + '' === values);
          if (range) {
            detail = {
              value: range[this.map.value],
              data: range,
            };
          }
        }
        this.formItem && this.formItem.setValue(detail.value);
        // TODO 兼容 vue2
        this.$emit('input', detail.value);
        // // TOTO 兼容 vue3
        this.$emit('update:modelValue', detail.value);
        this.$emit('change', {
          detail,
        });
        if (this.multiple) {
          // 如果 v-model 没有绑定 ，则走内部逻辑
          // if (this.value.length === 0) {
          this.dataList = this.getDataList(detail.value, true);
          // }
        } else {
          this.dataList = this.getDataList(detail.value);
        }
      },

      /**
       * 获取渲染的新数组
       * @param {Object} value 选中内容
       */
      getDataList(value) {
        // 解除引用关系，破坏原引用关系，避免污染源数据
        let dataList = JSON.parse(JSON.stringify(this.range));
        let list = [];
        if (this.multiple) {
          if (!Array.isArray(value)) {
            value = [];
          }
        }
        dataList.forEach((item, index) => {
          item.disabled = item.disable || item.disabled || false;
          if (this.multiple) {
            if (value.length > 0) {
              let have = value.find((val) => val === item[this.map.value]);
              item.selected = have !== undefined;
            } else {
              item.selected = false;
            }
          } else {
            item.selected = value === item[this.map.value];
          }

          list.push(item);
        });
        return this.setRange(list);
      },
      /**
       * 处理最大最小值
       * @param {Object} list
       */
      setRange(list) {
        let selectList = list.filter((item) => item.selected);
        let min = Number(this.min) || 0;
        let max = Number(this.max) || '';
        list.forEach((item, index) => {
          if (this.multiple) {
            if (selectList.length <= min) {
              let have = selectList.find((val) => val[this.map.value] === item[this.map.value]);
              if (have !== undefined) {
                item.disabled = true;
              }
            }

            if (selectList.length >= max && max !== '') {
              let have = selectList.find((val) => val[this.map.value] === item[this.map.value]);
              if (have === undefined) {
                item.disabled = true;
              }
            }
          }
          this.setStyles(item, index);
          list[index] = item;
        });
        return list;
      },
      /**
       * 设置 class
       * @param {Object} item
       * @param {Object} index
       */
      setStyles(item, index) {
        //  设置自定义样式
        item.styleBackgroud = this.setStyleBackgroud(item);
        item.styleIcon = this.setStyleIcon(item);
        item.styleIconText = this.setStyleIconText(item);
        item.styleRightIcon = this.setStyleRightIcon(item);
      },

      /**
       * 获取选中值
       * @param {Object} range
       */
      getSelectedValue(range) {
        if (!this.multiple) return this.dataValue;
        let selectedArr = [];
        range.forEach((item) => {
          if (item.selected) {
            selectedArr.push(item[this.map.value]);
          }
        });
        return this.dataValue && this.dataValue.length > 0 ? this.dataValue : selectedArr;
      },

      /**
       * 设置背景样式
       */
      setStyleBackgroud(item) {
        let styles = {};
        let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff';
        if (this.mode !== 'list') {
          styles['border-color'] = item.selected ? selectedColor : '#DCDFE6';
        }
        if (this.mode === 'tag') {
          styles['background-color'] = item.selected ? selectedColor : '#f5f5f5';
        }
        let classles = '';
        for (let i in styles) {
          classles += `${i}:${styles[i]};`;
        }
        return classles;
      },
      setStyleIcon(item) {
        let styles = {};
        let classles = '';
        let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff';
        styles['background-color'] = item.selected ? selectedColor : '#fff';
        styles['border-color'] = item.selected ? selectedColor : '#DCDFE6';

        if (!item.selected && item.disabled) {
          styles['background-color'] = '#F2F6FC';
          styles['border-color'] = item.selected ? selectedColor : '#DCDFE6';
        }

        for (let i in styles) {
          classles += `${i}:${styles[i]};`;
        }
        return classles;
      },
      setStyleIconText(item) {
        let styles = {};
        let classles = '';
        let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff';
        if (this.mode === 'tag') {
          styles.color = item.selected
            ? this.selectedTextColor
              ? this.selectedTextColor
              : '#fff'
            : '#666';
        } else {
          styles.color = item.selected
            ? this.selectedTextColor
              ? this.selectedTextColor
              : selectedColor
            : '#666';
        }
        if (!item.selected && item.disabled) {
          styles.color = '#999';
        }

        for (let i in styles) {
          classles += `${i}:${styles[i]};`;
        }
        return classles;
      },
      setStyleRightIcon(item) {
        let styles = {};
        let classles = '';
        if (this.mode === 'list') {
          styles['border-color'] = item.selected ? this.styles.selectedColor : '#DCDFE6';
        }
        for (let i in styles) {
          classles += `${i}:${styles[i]};`;
        }

        return classles;
      },
    },
  };
</script>

<style lang="scss">
  $checked-color: var(--ui-BG-Main);
  $border-color: #dcdfe6;
  $disable: 0.4;

  @mixin flex {
    /* #ifndef APP-NVUE */
    display: flex;
    /* #endif */
  }

  .uni-data-loading {
    @include flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    height: 36px;
    padding-left: 10px;
    color: #999;
  }

  .uni-data-checklist {
    position: relative;
    z-index: 0;
    flex: 1;
    // 多选样式
    .checklist-group {
      @include flex;
      flex-direction: row;
      flex-wrap: wrap;

      &.is-list {
        flex-direction: column;
      }

      .checklist-box {
        @include flex;
        flex-direction: row;
        align-items: center;
        position: relative;
        margin: 5px 0;
        margin-right: 25px;

        .hidden {
          position: absolute;
          opacity: 0;
        }

        // 文字样式
        .checklist-content {
          @include flex;
          flex: 1;
          flex-direction: row;
          align-items: center;
          justify-content: space-between;
          .checklist-text {
            font-size: 14px;
            color: #666;
            margin-left: 5px;
            line-height: 14px;
          }

          .checkobx__list {
            border-right-width: 1px;
            border-right-color: #007aff;
            border-right-style: solid;
            border-bottom-width: 1px;
            border-bottom-color: #007aff;
            border-bottom-style: solid;
            height: 12px;
            width: 6px;
            left: -5px;
            transform-origin: center;
            transform: rotate(45deg);
            opacity: 0;
          }
        }

        // 多选样式
        .checkbox__inner {
          /* #ifndef APP-NVUE */
          flex-shrink: 0;
          box-sizing: border-box;
          /* #endif */
          position: relative;
          width: 16px;
          height: 16px;
          border: 1px solid $border-color;
          border-radius: 4px;
          background-color: #fff;
          z-index: 1;
          .checkbox__inner-icon {
            position: absolute;
            /* #ifdef APP-NVUE */
            top: 2px;
            /* #endif */
            /* #ifndef APP-NVUE */
            top: 1px;
            /* #endif */
            left: 5px;
            height: 8px;
            width: 4px;
            border-right-width: 1px;
            border-right-color: #fff;
            border-right-style: solid;
            border-bottom-width: 1px;
            border-bottom-color: #fff;
            border-bottom-style: solid;
            opacity: 0;
            transform-origin: center;
            transform: rotate(40deg);
          }
        }

        // 单选样式
        .radio__inner {
          @include flex;
          /* #ifndef APP-NVUE */
          flex-shrink: 0;
          box-sizing: border-box;
          /* #endif */
          justify-content: center;
          align-items: center;
          position: relative;
          width: 16px;
          height: 16px;
          border: 1px solid $border-color;
          border-radius: 16px;
          background-color: #fff;
          z-index: 1;

          .radio__inner-icon {
            width: 8px;
            height: 8px;
            border-radius: 10px;
            opacity: 0;
          }
        }

        // 默认样式
        &.is--default {
          // 禁用
          &.is-disable {
            /* #ifdef H5 */
            cursor: not-allowed;
            /* #endif */
            .checkbox__inner {
              background-color: #f2f6fc;
              border-color: $border-color;
              /* #ifdef H5 */
              cursor: not-allowed;
              /* #endif */
            }

            .radio__inner {
              background-color: #f2f6fc;
              border-color: $border-color;
            }
            .checklist-text {
              color: #999;
            }
          }

          // 选中
          &.is-checked {
            .checkbox__inner {
              border-color: $checked-color;
              background-color: $checked-color;

              .checkbox__inner-icon {
                opacity: 1;
                transform: rotate(45deg);
              }
            }
            .radio__inner {
              border-color: $checked-color;
              .radio__inner-icon {
                opacity: 1;
                background-color: $checked-color;
              }
            }
            .checklist-text {
              color: $checked-color;
            }
            // 选中禁用
            &.is-disable {
              .checkbox__inner {
                opacity: $disable;
              }

              .checklist-text {
                opacity: $disable;
              }
              .radio__inner {
                opacity: $disable;
              }
            }
          }
        }

        // 按钮样式
        &.is--button {
          margin-right: 10px;
          padding: 5px 10px;
          border: 1px $border-color solid;
          border-radius: 3px;
          transition: border-color 0.2s;

          // 禁用
          &.is-disable {
            /* #ifdef H5 */
            cursor: not-allowed;
            /* #endif */
            border: 1px #eee solid;
            opacity: $disable;
            .checkbox__inner {
              background-color: #f2f6fc;
              border-color: $border-color;
              /* #ifdef H5 */
              cursor: not-allowed;
              /* #endif */
            }
            .radio__inner {
              background-color: #f2f6fc;
              border-color: $border-color;
              /* #ifdef H5 */
              cursor: not-allowed;
              /* #endif */
            }
            .checklist-text {
              color: #999;
            }
          }

          &.is-checked {
            border-color: $checked-color;
            .checkbox__inner {
              border-color: $checked-color;
              background-color: $checked-color;
              .checkbox__inner-icon {
                opacity: 1;
                transform: rotate(45deg);
              }
            }

            .radio__inner {
              border-color: $checked-color;

              .radio__inner-icon {
                opacity: 1;
                background-color: $checked-color;
              }
            }

            .checklist-text {
              color: $checked-color;
            }

            // 选中禁用
            &.is-disable {
              opacity: $disable;
            }
          }
        }

        // 标签样式
        &.is--tag {
          margin-right: 10px;
          padding: 5px 10px;
          border: 1px $border-color solid;
          border-radius: 3px;
          background-color: #f5f5f5;

          .checklist-text {
            margin: 0;
            color: #666;
          }

          // 禁用
          &.is-disable {
            /* #ifdef H5 */
            cursor: not-allowed;
            /* #endif */
            opacity: $disable;
          }

          &.is-checked {
            background-color: $checked-color;
            border-color: $checked-color;

            .checklist-text {
              color: #fff;
            }
          }
        }
        // 列表样式
        &.is--list {
          /* #ifndef APP-NVUE */
          display: flex;
          /* #endif */
          padding: 10px 15px;
          padding-left: 0;
          margin: 0;

          &.is-list-border {
            border-top: 1px #eee solid;
          }

          // 禁用
          &.is-disable {
            /* #ifdef H5 */
            cursor: not-allowed;
            /* #endif */
            .checkbox__inner {
              background-color: #f2f6fc;
              border-color: $border-color;
              /* #ifdef H5 */
              cursor: not-allowed;
              /* #endif */
            }
            .checklist-text {
              color: #999;
            }
          }

          &.is-checked {
            .checkbox__inner {
              border-color: $checked-color;
              background-color: $checked-color;

              .checkbox__inner-icon {
                opacity: 1;
                transform: rotate(45deg);
              }
            }
            .radio__inner {
              .radio__inner-icon {
                opacity: 1;
              }
            }
            .checklist-text {
              color: $checked-color;
            }

            .checklist-content {
              .checkobx__list {
                opacity: 1;
                border-color: $checked-color;
              }
            }

            // 选中禁用
            &.is-disable {
              .checkbox__inner {
                opacity: $disable;
              }

              .checklist-text {
                opacity: $disable;
              }
            }
          }
        }
      }
    }
  }
</style>
